import java.net.*;
import java.io.*;


/**
* It wraps around a Socket and provides methods for conveniently read or
* write to the streams without any risk of blocking the thread. It mimics
* (with some minor changes) the behaviour from Scanner for the read
* operations and PrintStream for the write operations. None of the methods
* will block except from the constructor which will return only when it have
* established an connection to the destination address or if an error
* occurs.
*/
public class EasySocket {
	private Socket socket;

	private StringBuffer buffer = new StringBuffer();

	private IOException exception = null;


	/**
	* Wraps an EasySocket around an existing Socket.
	*/
	public EasySocket(Socket socket) {
		this.socket = socket;
		try {
			socket.setTcpNoDelay(true);
		} catch(SocketException e) {
			e.printStackTrace();
		}
	}

	/**
	* Creates a new EasySocket which connects to an address
	*/
	public EasySocket(String address, int port) throws IOException {
		socket = new Socket(address, port);
		try {
			socket.setTcpNoDelay(true);
		} catch(SocketException e) {
			e.printStackTrace();
		}
	}

	/**
	* Creates a new EasySocket which connects to an address using a specified timeout
	*/
	public EasySocket(String address, int port, int timeout) throws IOException {
		socket = new Socket();
		socket.connect(new InetSocketAddress(address, port), timeout);
		try {
			socket.setTcpNoDelay(true);
		} catch(SocketException e) {
			e.printStackTrace();
		}
	}

	/**
	* Returns the most previous IOException that happened (if any).
	*/
	public IOException getLastException() {
		return exception;
	}

	/**
	* Gets the Socket which is used by this EasySocket.
	*/
	public Socket getSocket() {
		return socket;
	}

	/**
	* Updates the local buffer with data from the Socket.
	*/
	private void updateBuffer() {
		try {
			while (socket.getInputStream().available() > 0) {
				buffer.append((char)socket.getInputStream().read());
			}
		} catch (IOException e) {
			exception = e;
			System.err.println("IOException in EasySocket (updateBuffer()).");
		}
	}

	/**
	* Returns true if a space and/or newline exists in the stream.
	*/
	public boolean hasNext() {
		updateBuffer();
		return buffer.indexOf(" ") > -1 || buffer.indexOf("\n") > -1;
	}

	/**
	* Returns true if a newline exists in the stream.
	*/
	public boolean hasNextLine() {
		updateBuffer();
		return buffer.indexOf("\n") > -1;
	}

	/**
	* Returns the characters up until the next space or newline. It
	* will remove the characeters and the space/newline from the buffer.
	*/
	public String next() {
		if (hasNext()) {
			int indexSpace = buffer.indexOf(" ");
			int indexLine = buffer.indexOf("\n");
			int index = Math.min(Math.max(indexSpace, 0), Math.max(indexLine, 0));
			String str = buffer.substring(0, index);
			buffer.delete(0, index+1);
			return str.trim();
		} else {
			return "";
		}
	}

	/**
	* Equivalent to Integer.parseInt(EasySocket.next()) except that it will
	* return null if next() would return an empty string.
	*/
	public Integer nextInt() {
		String nextString = next();
		if (nextString.equals("")) {
			return null;
		}
		return Integer.parseInt(nextString);
	}

	/**
	* Returns the characeters up until the next newline. It will remove
	* the characters and the newline from the buffer.
	*/
	public String nextLine() {
		if (hasNextLine()) {
			int index = buffer.indexOf("\n");
			String str = buffer.substring(0, index);
			buffer.delete(0, index+1);
			return str.trim();
		} else {
			return "";
		}
	}

	/**
	* Directly outputs the string specified onto the Socket's OutputStream.
	* It will append a newline (\n) to the end of the string.
	*
	* @param str String to be written to the underlying Socket's OutputStream.
	*/
	public void println(String str) {
		print(str+"\n");
	}


	/**
	* Directly outputs the string specified onto the Socket's OutputStream.
	*
	* @param str String to be written to the underlying Socket's OutputStream.
	*/
	public void print(String str) {
		try {
			OutputStream os = socket.getOutputStream();
			os.write(str.getBytes());
			os.flush();
		} catch (IOException e) {
			exception = e;
			System.err.println("IOException in EasySocket (print(String) using TCP).");
		}
	}

	/**
	* Flushes the Socket's OutputStream. This method probably does nothing
	* since the documentation for OutputStream says it does nothing.
	*/
	public void flush() {
		try {
			OutputStream os = socket.getOutputStream();
			os.flush();
		} catch (IOException e) {
			exception = e;
			System.err.println("IOException in EasySocket (flush()).");
		}
	}

	/**
	* Closes the underlying Socket.
	*/
	public void close() {
		try {
			socket.close();
		} catch (IOException e) {
			exception = e;
			System.err.println("IOException in EasySocket (close()).");
		}
	}
}